﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class provides methods to map a quadratic function.
    /// </summary>
    [Serializable]
    public class QuadraticFunction : IQuadraticFunction
    {
        /// <summary>
        /// The first component of the quadratic function.
        /// </summary>
        private double a;

        /// <summary>
        /// The second component of the quadratic function.
        /// </summary>
        private double b;

        /// <summary>
        /// The third component of the quadratic function.
        /// </summary>
        private double c;

        /// <summary>
        /// Initializes a new instance of the <see cref="QuadraticFunction"/> class.
        /// </summary>
        public QuadraticFunction()
        {
            this.a = 1;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="QuadraticFunction"/> class.
        /// </summary>
        /// <param name="function">The function to clone.</param>
        public QuadraticFunction(QuadraticFunction function)
        {
            if (function == (QuadraticFunction) null)
            {
                throw new ArgumentNullException("function");
            }

            this.a = function.A;
            this.b = function.B;
            this.c = function.C;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="QuadraticFunction"/> class.
        /// </summary>
        /// <param name="a">The first component of the quadratic function.</param>
        /// <param name="b">The second component of the quadratic function.</param>
        /// <param name="c">The third component of the quadratic function.</param>
        public QuadraticFunction(double a, double b, double c)
        {
            this.a = a;
            this.b = b;
            this.c = c;
        }

        /// <summary>
        /// Gets or sets the first component of the quadratic function.
        /// </summary>
        /// <value>The first component of the quadratic function.</value>
        public double A
        {
            get { return a; }
            set { a = value; }
        }

        /// <summary>
        /// Gets or sets the second component of the quadratic function.
        /// </summary>
        /// <value>The second component of the quadratic function.</value>
        public double B
        {
            get { return b; }
            set { b = value; }
        }

        /// <summary>
        /// Gets or sets the third component of the quadratic function.
        /// </summary>
        /// <value>The third component of the quadratic function.</value>
        public double C
        {
            get { return c; }
            set { c = value; }
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first quadratic function.</param>
        /// <param name="b">The second quadratic function.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(QuadraticFunction a, QuadraticFunction b)
        {
            if (System.Object.ReferenceEquals(a, b))
            {
                return true;
            }

            if (((object) a == null) || ((object) b == null))
            {
                return false;
            }

            return ((a.A == b.A) && (a.B == b.B) && (a.C == b.C));
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first quadratic function.</param>
        /// <param name="b">The second quadratic function.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(QuadraticFunction a, QuadraticFunction b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first quadratic function.</param>
        /// <param name="b">The second quadratic function.</param>
        /// <returns>The result of the operator.</returns>
        public static QuadraticFunction operator +(QuadraticFunction a, QuadraticFunction b)
        {
            if (a == (QuadraticFunction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (QuadraticFunction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new QuadraticFunction(a.A + b.A, a.B + b.B, a.C + b.C);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first quadratic function.</param>
        /// <param name="b">The second quadratic function.</param>
        /// <returns>The result of the operator.</returns>
        public static QuadraticFunction operator -(QuadraticFunction a, QuadraticFunction b)
        {
            if (a == (QuadraticFunction) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (QuadraticFunction) null)
            {
                throw new ArgumentNullException("b");
            }

            return new QuadraticFunction(a.A - b.A, a.B - b.B, a.C - b.C);
        }

        /// <summary>
        /// Compares the current instance to another quadratic function.
        /// </summary>
        /// <param name="compare">The quadratic function to compare.</param>
        /// <returns>True if the functions are even otherwise, false.</returns>
        public bool CompareTo(QuadraticFunction compare)
        {
            return this == compare;
        }

        /// <summary>
        /// Transforms the current instance of a quadratic function to a polynomial.
        /// </summary>
        /// <returns>The transformed polynomial.</returns>
        public SimplePolynomial ToPolynomial()
        {
            return new SimplePolynomial(this.a, this.b, this.c);
        }

        /// <summary>
        /// Calculates the function f(x) at a specified x value.
        /// </summary>
        /// <param name="x">The specified x value.</param>
        /// <returns>The f(x) value at a specified x value.</returns>
        public double CalculateFunction(double x)
        {
            return this.a * Math.Pow(x, 2) + this.b * x + this.c;
        }

        /// <summary>
        /// Normalizes this quadratic function.
        /// </summary>
        public void Normalize()
        {
            this.a /= this.a;
            this.b /= this.a;
            this.c /= this.a;
        }

        /// <summary>
        /// Gets the apex of the quadratic function.
        /// </summary>
        /// <returns>The apex of the quadratic function.</returns>
        public Point2D GetApex()
        {
            if (this.a == 0)
            {
                throw new IllegalArithmeticException("Division by zero not allowed.");
            }

            Point2D result = new Point2D();

            result.X = -(this.b / 2 * this.a);
            result.Y = (4 * this.a * this.c - Math.Pow(this.b, 2)) / 4 * this.a;

            return result;
        }

        /// <summary>
        /// Gets the roots of the quadratic function. If the function has no roots the
        /// method will return null.
        /// </summary>
        /// <returns>The roots of the quadratic function.</returns>
        public double[] GetRoots()
        {
            return ExtendedMath.SolveQuadraticEquation(this.b, this.c);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return this.a + "x² + " + this.b + "x + " + this.c;
        }
    }
}